package com.meshnetics.gb.type.impl.additional;

import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.DataType;
import com.meshnetics.gb.type.impl.StringUtils;
import com.meshnetics.gb.util.ByteBufferManager;

import java.nio.ByteBuffer;

/**
 * Helper class for forming String representation, serialize of DataValue-s
 * array, calculate hash of DataValue-s array and calculate data size of object.
 *
 * @author Yury Aserchev (yaserchev@meshnetics.com)
 *         Date: 27.08.2008 18:43:13
 */
public final class HelperImmutableObject {
    public static String toString(final String header, final DataValue[] values) {
        if (header == null) {
            throw new NullPointerException("parameter header is null");
        }
        if (values == null) {
            throw new NullPointerException("parameter values is null");
        }
        final StringBuffer buff = new StringBuffer(header);
        int cnt = 0;
        // count not null values
        if (values.length > 0) {
            for (int i = 0; i < values.length; i++) {
                if (values[i] != null) {
                    cnt ++;
                }
            }
        }
        if (cnt > 0) {
            buff.append("[");
            for (int i = 0; i < values.length; i++) {
                final DataValue value = values[i];
                if (value != null) {
                    if (value instanceof Enumeration) {
                        buff.append(((Enumeration) value).getSimpleString());
                    } else {
                        buff.append(value.toString());
                    }
                    if (-- cnt > 0) {
                        buff.append(", ");
                    }
                }
            }
            buff.append("]");
        }
        return buff.toString();
    }

    public static void serialize(final ByteBuffer buffer, final DataValue[] values) {
        if (buffer == null) {
            throw new NullPointerException("parameter buffer is null");
        }
        if (values == null) {
            throw new NullPointerException("parameter values is null");
        }
        if (values.length > 0) {
            for (int i = 0; i < values.length; i++) {
                final DataValue value = values[i];
                if (value != null) {
                    value.serialize(buffer);
                }
            }
        }
    }

    public static int calculateHash(final DataValue[] values) {
        if (values == null) {
            throw new NullPointerException("parameter values is null");
        }
        int res = 0;
        if (values.length > 0) {
            for (int i = 0; i < values.length; i++) {
                final DataValue value = values[i];
                if (value != null) {
                    res = res * 29 + value.hashCode();
                }
            }
        }
        return res;
    }

    public static int calculateSize(final DataValue[] values) {
        if (values == null) {
            throw new NullPointerException("parameter values is null");
        }
        int res = 0;
        if (values.length > 0) {
            for (int i = 0; i < values.length; i++) {
                final DataValue value = values[i];
                if (value != null) {
                    res += value.getDataSize();
                }
            }
        }
        return res;
    }

    public static byte[] serializeToBytes(final DataValue[] values) {
        if (values == null) {
            throw new NullPointerException("parameter values is null");
        }
        final ByteBuffer buffer = ByteBufferManager.allocateBE(calculateSize(values));
        serialize(buffer, values);
        return buffer.array();
    }

    public static String serializetoString(final DataValue[] values) {
        return StringUtils.toHexString(serializeToBytes(values));
    }

    public static boolean isDataEqual(final DataValue[] first, final DataValue[] second) {
        if (first == null) {
            throw new NullPointerException("parameter first is null");
        }
        if (second == null) {
            throw new NullPointerException("parameter second is null");
        }
        if (first.length != second.length) {
            return false;
        }
        boolean res = true;
        for (int i = 0; i < first.length; i++) {
            if (!areObjectsEqual(first[i], second[i])) {
                res = false;
                break;
            }
        }
        return res;
    }

    private static boolean areObjectsEqual(final Object one, final Object two) {
        if ((one == null && two != null) || (one != null && two == null)) {
            return false;
        }
        return one == null && two == null || (one.getClass() == two.getClass() && one.equals(two));
    }

    public static DataValue[] mergeArrays(final DataValue[] first,
                                          final DataValue[] second) {
        if (second == null) {
            return (DataValue[]) first.clone();
        }
        final DataValue[] res = new DataValue[first.length + second.length];
        System.arraycopy(first, 0, res, 0, first.length);
        System.arraycopy(second, 0, res, first.length, second.length);
        return res;
    }

    public static DataValue[] restoreFromBuffer(final DataType type, final int count, final ByteBuffer buffer) {
        if (type == null) {
            throw new NullPointerException("parameter type is null");
        }
        if (count < 0) {
            throw new IllegalArgumentException("count cannot be less than zero (" + count + ")");
        }
        final DataValue[] res = new DataValue[count];
        if (count > 0) {
            for (int i = 0; i < res.length; i++) {
                res[i] = type.deserialize(buffer);
            }
        }
        return res;
    }

    private HelperImmutableObject(){}
}
